#pragma once
#include "CGIVulkanDevice.h"
namespace Alice{
    class CGIVulkanQueue;
    class CGIVulkanCommandBufferPool;
    class CGIVulkanCommandListContext;
    class VulkanSemaphore;
    class VulkanFence;
    class VulkanLayoutManager;
    class CGIVulkanCommandBufferManager;
    class CGIVulkanCommandBuffer{
    public:
        CGIVulkanDevice* mDevice;
        VkCommandBuffer mCommandBuffer;
        enum class EState : uint8
        {
            ReadyForBegin,
            IsInsideBegin,
            IsInsideRenderPass,
            HasEnded,
            Submitted,
            NotAllocated,
            NeedReset,
        };
        EState State;
        uint8 bNeedsDynamicStateSet			: 1;
        uint8 bHasPipeline					: 1;
        uint8 bHasViewport					: 1;
        uint8 bHasScissor					: 1;
        uint8 bHasStencilRef				: 1;
        uint8 bIsUploadOnly					: 1;
        uint8 bIsUniformBufferBarrierAdded	: 1;
        CGIVulkanCommandBufferPool* mCommandBufferPool;
        VulkanLayoutManager* mLayoutManager;
        std::vector<VkViewport> mCurrentViewports;
        std::vector<VkRect2D> mCurrentScissors;
        VulkanFence* mFence;
        std::vector<VkPipelineStageFlags> mWaitFlags;
        std::vector<VulkanSemaphore*> mWaitSemaphores;
	    std::vector<VulkanSemaphore*> mSubmittedWaitSemaphores;
        CGIVulkanCommandBuffer(CGIVulkanDevice* InDevice, CGIVulkanCommandBufferPool* InCommandBufferPool, bool bInIsUploadOnly);
        inline VkCommandBuffer GetHandle()
        {
            return mCommandBuffer;
        }
        inline VulkanLayoutManager& GetLayoutManager()
        {
            return *mLayoutManager;
        }
        inline bool IsInsideRenderPass() const
        {
            return State == EState::IsInsideRenderPass;
        }

        inline bool IsOutsideRenderPass() const
        {
            return State == EState::IsInsideBegin;
        }

        inline bool HasBegun() const
        {
            return State == EState::IsInsideBegin || State == EState::IsInsideRenderPass;
        }

        inline bool HasEnded() const
        {
            return State == EState::HasEnded;
        }

        inline bool IsSubmitted() const
        {
            return State == EState::Submitted;
        }
        void AllocMemory();
        void RefreshFenceStatus();
        void Begin();
        void End();
        void InitializeTimings(CGIVulkanCommandListContext* InContext);
        void MarkSemaphoresAsSubmitted()
        {
            mWaitFlags.clear();
            // Move to pending delete list
            mSubmittedWaitSemaphores = mWaitSemaphores;
            mWaitSemaphores.clear();
        }
        void AddWaitSemaphore(VkPipelineStageFlags inWaitFlags, VulkanSemaphore* inWaitSemaphore);
        void AddWaitSemaphores(VkPipelineStageFlags inWaitFlags, std::vector<VulkanSemaphore*> inWaitSemaphores);
        CGIVulkanCommandBufferPool* GetOwner()
        {
            return mCommandBufferPool;
        }
    };
    class CGIVulkanCommandBufferPool
    {
    public:
        VkCommandPool mHandle;
        CGIVulkanDevice* mDevice;
        std::vector<CGIVulkanCommandBuffer*> mCmdBuffers;
        std::vector<CGIVulkanCommandBuffer*> mFreeCmdBuffers;
        CGIVulkanCommandBufferManager& mMgr;
        CGIVulkanCommandBufferPool(CGIVulkanDevice* InDevice, CGIVulkanCommandBufferManager& InMgr);
        CGIVulkanCommandBuffer* Create(bool bIsUploadOnly);
        void Create(uint32 QueueFamilyIndex);
        inline VkCommandPool GetHandle() const
        {
            return mHandle;
        }
        void RefreshFenceStatus(CGIVulkanCommandBuffer* SkipCmdBuffer = nullptr);
        inline CGIVulkanCommandBufferManager& GetMgr()
        {
            return mMgr;
        }
    };
    class CGIVulkanCommandBufferManager
    {
    public:
        CGIVulkanDevice* mDevice;
        CGIVulkanCommandListContext* mContext;
        CGIVulkanCommandBufferPool mPool;
        CGIVulkanQueue* mQueue;
        CGIVulkanCommandBuffer* mActiveCmdBuffer;
        CGIVulkanCommandBuffer* mUploadCmdBuffer;
        VulkanSemaphore* mUploadCmdBufferSemaphore;
        CGIVulkanCommandBufferManager(CGIVulkanDevice* InDevice, CGIVulkanCommandListContext* InContext);
        ~CGIVulkanCommandBufferManager();
        void Init(CGIVulkanCommandListContext* InContext);
        inline CGIVulkanCommandListContext* GetCommandListContext()
        {
            return mContext;
        }
        
        inline CGIVulkanQueue* GetQueue()
        {
            return mQueue;
        }
        CGIVulkanCommandBuffer* GetUploadCmdBuffer();
        void SubmitUploadCmdBuffer(uint32 NumSignalSemaphores = 0, VkSemaphore* SignalSemaphores = nullptr);
    };
}